﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TheMovieDB;
using System.IO;
using HowToUse_Dll;
using MediaInfoLib;
using System.Xml.Serialization;
using System.Drawing;
using TMS.Properties;

namespace TMS
{
    delegate void loadmediainfodelegate(string _filename);
    
    [Serializable]    
    public class TMSMovie : ICloneable
    {
        string _filename;
        private string progressstatus = "";

        [NonSerialized]
        TmdbAPI tmdbAPI;
        //TmdbMovie myMovie;
        [NonSerialized]
        FileMetadata fileMetadata;
        private string friendlyName = "";
        private bool mediainfoloaded = false;

        [XmlIgnore]
        public bool IsLoading
        {
            get
            {
                return MovieInfoState == TMDBMovieInfoState.Searching;
            }
        }

        [XmlIgnore]
        public Bitmap LoadingImage
        {
            get
            {
                if (IsLoading)
                    return Resources.loading1;
                else
                    return null;
            }
        }

        #region Properties

        public string UniqueID
        {
            get;
            set; //private set;
        }
        public string FriendlyName
        {
            get 
            {
                return friendlyName;
            }
            set 
            {
                friendlyName = value;
            }
        }
        public string CollectionName 
        { 
            set; //set; //private set; 
            get; }
        public string ProgressStatus { 
            set
            {
              progressstatus = value;
              ForceRefreshInfo();
            } //private set;
            get 
            {
                return progressstatus;
            }
        }
        public TMDBMovieInfoState MovieInfoState
        {
            get;
            set; //private set;
        }
        public string UserName
        {
            get;
            set;
        }
        public bool LoadedFromDatabase
        {
            get;
            set; //private set;
        }
        public string ProxyAddress
        {
            get;
            set;
        }
        
        #region TMDB Properties

        private string imdbID;
        private decimal score;
        private decimal popularity;
        private bool? translated;
        private string language;
        private string filmname;
        private string alternativeName;
        private string type;
        private int id;
        private string imdbId;
        private string url;
        private decimal rating;
        private string certification;
        private string overview;
        private string releasedString;
        private DateTime? released;
        private string movieRuntime;
        private TimeSpan? runtime;
        private string budget;
        private string revenue;
        private string homepage;
        private string trailer;
        private List<TmdbCategory> categories;
        private List<TmdbStudio> studios;
        private List<TmdbCountry> countries;
        private List<TmdbImage> images;
        private List<TmdbCastPerson> cast;
        private string lastModifiedString;
        private DateTime? lastModified;
        private DateTime? addedOn;

        public string ImdbID
        {
            get
            {
                return imdbId;
            }
            set 
            {
                imdbId = value;
            }
        }
        public string Filename
        {
            get { return _filename; }
            set 
            {
                _filename = value;
            }
        }
        public decimal Score
        {
            get
            {
                return score;
            }
            set
            {
                score = value;
            }
        }
        public decimal Popularity
        {
            get
            {
                return popularity;
            }
            set
            {
              popularity  = value;
            }
        }
        public bool? Translated
        {
            get
            {
                return translated;
            }
            set
            {
                translated= value;
            }
        }
        public string Language
        {
            get
            {
                return language;
            }
            set
            {
                language= value;
            }
        }
        public string FilmName
        {
            get
            {
                return filmname;
            }
            set
            {
                filmname = value;
            }
        }
        public string AlternativeName
        {
            get
            {
                return alternativeName;
            }
            set
            {
                alternativeName = value;
            }
        }
        public string Type
        {
            get
            {
                return type;
            }
            set
            {
                type= value;
            }
        }
        public int Id
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }
        public string Url
        {
            get
            {
                return url;
            }
            set
            {
                url = value;
            }
        }
        public decimal Rating
        {
            get
            {
                return rating;
            }
            set
            {
                rating = value;
            }
        }
        public string Certification
        {
            get
            {
                return certification;
            }
            set
            {
                certification = value;
            }
        }
        public string Overview
        {
            get
            {
                return overview;
            }
            set
            {
                overview = value;
            }
        }
        public string ReleasedString
        {
            get
            {
                return releasedString;
            }
            set
            {
                releasedString = value;
            }
        }
        public DateTime? Released
        {
            get
            {
                return released;
            }
            set
            {
                released = value;
            }
        }
        public string MovieRuntime
        {
            get
            {
                return movieRuntime;
            }
            set
            {
                movieRuntime = value;
            }
        }
        public TimeSpan? Runtime
        {
            get
            {
                return runtime;
            }
            set
            {
                runtime = value;
            }
        }
        public string Budget
        {
            get
            {
                return budget;
            }
            set
            {
                budget = value;
            }
        }
        public string Revenue
        {
         get
         {
            return revenue;
         }
            set
            {
                revenue = value;
            }
        }
        public string Homepage
        {
            get
            {
                return homepage;
            }
            set
            {
                homepage = value;
            }
        }
        public string Trailer
        {
            get
            {
                return trailer;
            }
            set
            {
                trailer = value;
            }
        }
        public List<TmdbCategory> Categories
        {
            get
            {
                return categories;
            }
            set
            {
                categories = value;
            }
        }
        public List<TmdbStudio> Studios
        {
            get
            {
                return studios;
            }
            set
            {
                studios = value;
            }
        }
        public List<TmdbCountry> Countries
        {
            get
            {
                return countries;
            }
            set
            {
                countries = value;
            }
        }
        public List<TmdbImage> Images
        {
            get
            {
                return images;
            }
            set
            {
                images = value;
            }
        }
        public List<TmdbCastPerson> Cast
        {
            get
            {
                return cast;
            }
            set
            {
                cast = value;
            }
        }
        public string LastModifiedString
        {
            get
            {
                return lastModifiedString;
            }
            set
            {
                lastModifiedString = value;
            }
        }
        public DateTime? LastModified
        {
            get
            {
                return lastModified;
            }
            set 
            {
                lastModified = value;
            }
        }
        #endregion
        #region Media Info Properties
        private string fileSize;
        private string formatInfo;
        private string duration;
        private string resolution;
        private List<VideoStreamsMetadata> videoStreams;
        private List<AudioStreamsMetadata> audioStreams;
        private List<TextStreamsMetadata> textStreams;

        public string FileSize
        {
            get
            {
                return fileSize;
            }
            set 
            { 
                fileSize = value; 
            }
        }
        public string FormatInfo
        {
            get
            {
                return formatInfo;
            }
            set 
            {
                formatInfo = value;
            }
        }

        public string Duration
        {
            get
            {
                return duration;
            }
            set 
            {
                duration = value;
            }
        }
        public string Resolution
        {
            get
            {
                return resolution;
            }
            set { resolution = value; }
        }
        public List<VideoStreamsMetadata> VideoStreams
        {
            get { return videoStreams; }
            set { videoStreams = value; }
        }

        public List<AudioStreamsMetadata> AudioStreams
        {
            get { return audioStreams; }
            set { audioStreams = value; }
        }
        public List<TextStreamsMetadata> TextStreams
        {
            get { return textStreams; }
            set { textStreams = value; }
        }

        public DateTime? AddedOn
        {
            get
            {
                return addedOn;
            }
            set 
            {
                if (addedOn == null)
                    addedOn = File.GetCreationTime(this._filename);
                else 
                    addedOn = value;
            }
        }

        #endregion 

        #endregion

        public event EventHandler OnInfoReady;
        public event EventHandler OnInfoRefreshed;

        public TMSMovie(string collectionName, string filename, string proxy) 
        {
            CollectionName = collectionName;
            ProxyAddress = proxy;
            //tmdbAPI = new TmdbAPI("958508f40a0cb941b485cdd1e15afdda");
            //tmdbAPI.GetMovieInfoCompleted += new TmdbAPI.MovieInfoAsyncCompletedEventHandler(tmdbAPI_GetMovieInfoCompleted);
            
            _filename = filename;

            Guid guid = Guid.NewGuid();
            UniqueID = guid.ToString();

            loadapi();
        }
        public TMSMovie() 
        {
            loadapi();
        }

        private void loadapi() 
        {
            tmdbAPI = new TmdbAPI("958508f40a0cb941b485cdd1e15afdda");
            tmdbAPI.GetMovieInfoCompleted += new TmdbAPI.MovieInfoAsyncCompletedEventHandler(tmdbAPI_GetMovieInfoCompleted);
            tmdbAPI.ProxyAddress = ProxyAddress;

            

        }
        void tmdbAPI_GetMovieInfoCompleted(object sender, ImdbMovieInfoCompletedEventArgs e)
        {
            try
            {
                ProgressStatus = "Reading TMDB Values ...";
                loadpropertiesfromTMDBMovie(e.Movie);
                if (String.IsNullOrEmpty(friendlyName))
                    friendlyName = this.FilmName;
                
               // MovieInfoState = TMDBMovieInfoState.InfoFound;

                ProgressStatus = "Loaded Completed";
                if (loadmediainfo(_filename, false))
                {
                    MovieInfoState = TMDBMovieInfoState.InfoFound;
                    ProgressStatus = "Ready";
                }
                
            }
            catch (Exception ex)
            {
                ProgressStatus = "Error";
            }

            if (OnInfoReady != null)
                OnInfoReady(this, null);
        }

        private void loadpropertiesfromTMDBMovie(TmdbMovie movie) 
        {
            imdbID = movie.ImdbId;
            score = movie.Score;
            popularity = movie.Popularity;
            translated = movie.Translated;
            language = movie.Language;
            filmname = movie.Name;
            alternativeName = movie.AlternativeName;
            type = movie.Type;
            id = movie.Id;
            imdbId = movie.ImdbId;
            url = movie.Url;
            rating = movie.Rating;
            certification = movie.Certification;
            overview = movie.Overview;
            releasedString = movie.ReleasedString;
            released = movie.Released;
            movieRuntime = movie.MovieRuntime;
            runtime = movie.Runtime;
            budget = movie.Budget;
            revenue = movie.Revenue;
            homepage = movie.Homepage;
            trailer = movie.Trailer;
            categories = movie.Categories;
            studios = movie.Studios;
            countries = movie.Countries;
            images = movie.Images;
            cast = movie.Cast;
            lastModifiedString = movie.LastModifiedString;
            lastModified = movie.LastModified;
        }

        void MovieInfoNotFound() 
        {
            try
            {
                //myMovie = new TmdbMovie();
                filmname = "*** Not Found [" + Path.GetFileNameWithoutExtension(_filename) + "]";
                ProgressStatus = "Info Not Found";
                MovieInfoState = TMDBMovieInfoState.InfoNotFound;
                loadmediainfo(_filename,true);
            }
            catch (Exception)
            {

            }

            if (OnInfoReady != null)
                OnInfoReady(this, null);
        
        }
        public void LoadMovieInfo() 
        {
            if (loadfromDatabase())
            {
                LoadedFromDatabase = true;
                ProgressStatus = "Loaded from Database";
            }

            try
            {
                ProgressStatus = "Search Imdb ID ...";
                string filmTitle = "";
                bool canbefilmId = false;

                if (String.IsNullOrEmpty(friendlyName))
                {
                    filmTitle = Path.GetFileNameWithoutExtension(_filename);
                }
                else 
                {
                    filmTitle = friendlyName;
                    canbefilmId = true;
                }

                AddedOn = File.GetCreationTime(_filename);

                UserName = Environment.UserName;
                MovieInfoState = TMDBMovieInfoState.Searching;
                TmdbMovie mov = tmdbAPI.MovieSearch(filmTitle).ToList().FirstOrDefault();
                
                if (mov ==null)
                    if (canbefilmId)
                        mov = tmdbAPI.MovieSearchByImdb(filmTitle).ToList().FirstOrDefault();

                ProgressStatus = "Loading Movie TMDB Info";
                if (mov != null)
                    tmdbAPI.GetMovieInfoAsync(mov.Id);
                else
                    MovieInfoNotFound();
            }
            catch (Exception ex)
            {
                MovieInfoState = TMDBMovieInfoState.ErrorOccured;
                tmdbAPI_GetMovieInfoCompleted(this, new ImdbMovieInfoCompletedEventArgs(new TmdbMovie() { Name = "*** Error [" + ex.Message + "]" }, null, false, null));
            }

            //loadmediainfo(_filename);

            //loadmediainfodelegate del = new loadmediainfodelegate(loadmediainfo);
            //del.BeginInvoke(_filename, null, null);
        }
        
        private bool loadfromDatabase()
        {
            return false;
        }

        private bool loadmediainfo(string filePath,bool donotupdatestate) 
        {
            bool returnvalue = true;
            try
            {
                ProgressStatus = "Loading Media Info";
                MediaInfo MI = new MediaInfo();
                fileMetadata = new FileMetadata();
                fileMetadata.AudioStreamsMetadata = new System.Collections.Generic.List<AudioStreamsMetadata>();
                fileMetadata.VideoStreamsMetadata = new System.Collections.Generic.List<VideoStreamsMetadata>();
                fileMetadata.TextStreamsMetadata = new System.Collections.Generic.List<TextStreamsMetadata>();

                MI.Open(filePath);

                int noOfVideoStreams = MI.Count_Get(StreamKind.Video);
                if (noOfVideoStreams == 0)
                    throw new Exception("No video streams found");

                int noOfAudioStreams = MI.Count_Get(StreamKind.Audio);
                int noOfTextStreams = MI.Count_Get(StreamKind.Text);

                //var info = MI.Inform();

                fileMetadata.Filename = MI.Get(StreamKind.General, 0, "FileName");
                fileMetadata.FileSize = MI.Get(StreamKind.General, 0, "FileSize/String");
                fileMetadata.Format = MI.Get(StreamKind.General, 0, "Format");
                fileMetadata.FormatInfo = MI.Get(StreamKind.General, 0, "Format/Info");
                fileMetadata.Duration = MI.Get(StreamKind.General, 0, "Duration/String1");

                formatInfo = fileMetadata.FormatInfo;
                fileSize = fileMetadata.FileSize;
                duration= fileMetadata.Duration;
                

                for (int videoStream = 0; videoStream < noOfVideoStreams; videoStream++)
                {
                    
                    VideoStreamsMetadata videoStreamsMetadata = new VideoStreamsMetadata();
                    videoStreamsMetadata.VideoID = videoStream;
                    videoStreamsMetadata.Format = GetMetaDataSection(StreamKind.Video, "Format", videoStream, MI);
                    videoStreamsMetadata.FormatInfo = GetMetaDataSection(StreamKind.Video, "Format/Info", videoStream, MI);
                    videoStreamsMetadata.FormatProfile = GetMetaDataSection(StreamKind.Video, "Format_Profile", videoStream, MI);
                    videoStreamsMetadata.DisplayAspectRatio = GetMetaDataSection(StreamKind.Video, "DisplayAspectRatio/String", videoStream, MI);
                    videoStreamsMetadata.Language = GetMetaDataSection(StreamKind.Video, "Language/String", videoStream, MI);
                    videoStreamsMetadata.Resolution = GetMetaDataSection(StreamKind.Video, "Width", videoStream, MI) + "x" + GetMetaDataSection(StreamKind.Video, "Height", videoStream, MI);
                    videoStreamsMetadata.BitRate = GetMetaDataSection(StreamKind.Video, "BitRate/String", videoStream, MI);
                    videoStreamsMetadata.Duration = GetMetaDataSection(StreamKind.Video, "Duration/String1", videoStream, MI);
                    fileMetadata.VideoStreamsMetadata.Add(videoStreamsMetadata);
                    if (videoStream == 0)
                        resolution = videoStreamsMetadata.Resolution;
                }

                for (int audioStream = 0; audioStream < noOfAudioStreams; audioStream++)
                {
                    AudioStreamsMetadata audioStreamsMetadata = new AudioStreamsMetadata();
                    audioStreamsMetadata.AudioID = audioStream;
                    audioStreamsMetadata.Format = GetMetaDataSection(StreamKind.Audio, "Format", audioStream, MI);
                    audioStreamsMetadata.FormatInfo = GetMetaDataSection(StreamKind.Audio, "Format/Info", audioStream, MI);
                    audioStreamsMetadata.Channels = GetMetaDataSection(StreamKind.Audio, "Channel(s)", audioStream, MI);
                    audioStreamsMetadata.Bitrate = GetMetaDataSection(StreamKind.Audio, "BitRate/String", audioStream, MI);
                    audioStreamsMetadata.SamplingRate = GetMetaDataSection(StreamKind.Audio, "SamplingRate/String", audioStream, MI);
                    audioStreamsMetadata.Compression_Mode = GetMetaDataSection(StreamKind.Audio, "Compression_Mode/String)", audioStream, MI);
                    audioStreamsMetadata.Language = GetMetaDataSection(StreamKind.Audio, "Language/String", audioStream, MI);
                    audioStreamsMetadata.Duration = GetMetaDataSection(StreamKind.Audio, "Duration/String1", audioStream, MI);
                    fileMetadata.AudioStreamsMetadata.Add(audioStreamsMetadata);
                }

                for (int textStream = 0; textStream < noOfTextStreams; textStream++)
                {
                    TextStreamsMetadata textStreamsMetadata = new TextStreamsMetadata();
                    textStreamsMetadata.TextID = textStream;
                    textStreamsMetadata.Format = GetMetaDataSection(StreamKind.Text, "Format", textStream, MI);
                    textStreamsMetadata.CodecInfo = GetMetaDataSection(StreamKind.Text, "Codec/Info", textStream, MI);
                    textStreamsMetadata.Language = GetMetaDataSection(StreamKind.Text, "Language/String", textStream, MI);
                    fileMetadata.TextStreamsMetadata.Add(textStreamsMetadata);
                }

                videoStreams = fileMetadata.VideoStreamsMetadata;
                audioStreams = fileMetadata.AudioStreamsMetadata;
                textStreams = fileMetadata.TextStreamsMetadata;

                MI.Close();
                ProgressStatus = "Ready";
                mediainfoloaded = true;
            }
            catch (Exception Ex)
            {
                returnvalue = false;
                //throw Ex;
            }

            if (!donotupdatestate)
                MovieInfoState = TMDBMovieInfoState.InfoFound;

            return returnvalue;
        }

        private string GetMetaDataSection(StreamKind streamKind, string metaTag, int streamID, MediaInfo MI)
        {
            return MI.Get(streamKind, streamID, metaTag);
        }
        public void ForceRefreshInfo() 
        {
            if (OnInfoRefreshed != null)
                OnInfoRefreshed(this, null);
        }
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

    public enum TMDBMovieInfoState 
    {
        Searching,
        InfoFound,
        InfoNotFound,
        ErrorOccured
    }
}
